package com.xuexi.mashibing;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 两个线程交替输出打印
 */
public class AlternatePrintExample {


    public static void main(String[] args) {
//        useLockSupport();
//
//        useWaitNotify();

//        useWaitNotify2();

//        useWaitNotify3();

        reentrantLockCondition();
    }


    static Thread t1 = null, t2 = null;
    static Thread t3 = null, t4 = null;

    private static void useLockSupport() {
        char[] num = "12345678".toCharArray();
        char[] alphabet = "ABCDEFGH".toCharArray();

        t1 = new Thread(() -> {
            for (char c : num) {
                System.out.print(c);
                //先叫醒t2，然后阻塞自己
                LockSupport.unpark(t2);
                LockSupport.park(t1);
            }
        }, "t1");

        t2 = new Thread(() -> {
            for (char c : alphabet) {
                //先将自己挂起，方式先输出字母
                LockSupport.park(t2);
                System.out.print(c);
                LockSupport.unpark(t1);
            }
        }, "t2");

        t2.start();
        t1.start();
    }


    private static final Object lock = new Object();
    private static boolean isNumberTurn = true;

    private static void useWaitNotify() {
        Thread numberThread = new Thread(new NumberPrinter());
        Thread letterThread = new Thread(new LetterPrinter());
        numberThread.start();
        letterThread.start();
    }

    static class NumberPrinter implements Runnable {
        @Override
        public void run() {
            for (int i = 1; i <= 26; i++) {
                synchronized (lock) {
                    while (!isNumberTurn) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(i);
                    isNumberTurn = false;
                    lock.notify();
                }
            }
        }
    }

    static class LetterPrinter implements Runnable {
        @Override
        public void run() {
            for (char c = 'A'; c <= 'Z'; c++) {
                synchronized (lock) {
                    while (isNumberTurn) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.print(c);
                    isNumberTurn = true;
                    lock.notify();
                }
            }
        }
    }

    private static void useWaitNotify2() {
        char[] num = "1234567".toCharArray();
        char[] alphabet = "ABCDEFG".toCharArray();
        final Object o = new Object();

        new Thread(() -> {
            synchronized (o) {
                for (char c : num) {
                    System.out.print(c);
                    //唤醒等待队列中的某个线程
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                o.notify();
            }
        }, "t3").start();

        new Thread(() -> {
            synchronized (o) {
                for (char c : alphabet) {
                    System.out.print(c);
                    //唤醒等待队列中的某个线程
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                o.notify();
            }
        }, "t4").start();
    }

    private static void useWaitNotify3() {
        char[] num = "1234567".toCharArray();
        char[] alphabet = "ABCDEFG".toCharArray();
        final Object o = new Object();
        CountDownLatch latch = new CountDownLatch(1);

        new Thread(() -> {
            //让打印字母的线程先等待
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            synchronized (o) {
                for (char c : alphabet) {
                    System.out.print(c);
                    //唤醒等待队列中的某个线程
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                o.notify();
            }
        }, "t6").start();


        new Thread(() -> {
            synchronized (o) {
                for (char c : num) {
                    System.out.print(c);
                    //打印数字的线程打印一个数字后，调用latch.countDown() 将latch等待中的线程放行
                    latch.countDown();

                    //唤醒等待队列中的某个线程
                    o.notify();
                    try {
                        o.wait();
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
                o.notify();
            }
        }, "t5").start();
    }

    /**
     * ReentrantLock 代替 synchronized
     * condition 理解为队列
     */
    private static void reentrantLockCondition() {
        char[] num = "1234567".toCharArray();
        char[] alphabet = "ABCDEFG".toCharArray();
        final Object o = new Object();

        Lock lock = new ReentrantLock();
        Condition conditionT1 = lock.newCondition();
        Condition conditionT2 = lock.newCondition();


        new Thread(() -> {
            lock.lock();
            try {
                for (char c : num) {
                    System.out.print(c);
                    conditionT2.signal(); // notify
                    conditionT1.await(); // wait
                }
                conditionT2.signal(); // notify
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t7").start();

        new Thread(() -> {
            lock.lock();
            try {
                for (char c : alphabet) {
                    System.out.print(c);
                    conditionT1.signal(); // notify
                    conditionT2.await(); // wait
                }
                conditionT1.signal(); // notify
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        }, "t8").start();
    }

}
